#include <iostream>
#include <string>
#include <queue>
#include "Mutex.hpp"
#include "Cond.hpp"
using namespace std;
using namespace LockModule;
using namespace CondModule;


namespace BlockQueueModule
{
  // 队列容量
  static const int gcap = 10;
  template <typename T>
  // 阻塞队列充当生产者消费者模型的 1 -- 交易场所
  class BlockQueue
  {
  private:
    // 判满
    bool IsFull() { return _q.size() == _cap; }
    // 判空
    bool IsEmpty() { return _q.empty(); }

  public:
    // 构造
    BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
    {
    }
    void Equeue(const T &in) // 生产者 制造数据
    {
      // 对临界区上锁
      LockGuard lockguard(_mutex);
      // 你想放数据，就能放数据吗？？生产数据是有条件的！
      // 结论1: 在临界区中等待是必然的(目前) 因为要想制造数据必须要访问临界区(阻塞队列)
      while (IsFull()) // 5. 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
      {
        std::cout << "生产者进入等待" << std::endl;
        _pwait_num++;
        _productor_cond.Wait(_mutex); // 2.进入等待释放锁 出等待再加锁

      // 3. 返回，线程被唤醒&&重新申请并持有锁(它会在临界区内醒来！)
      std:
        cout << "生产者被唤醒" << std::endl;
      }
      // 4. while(IsFull())不满足 || 线程被唤醒
      // 生产
      _q.push(in);

      // 肯定有数据！ 此时经过判断消费者等待计数器唤醒消费者
      if (_cwait_num)
      {
        std::cout << "叫醒消费者" << std::endl;
        _consumer_cond.Notify();
      }
      
    }
    void Pop(T *out) // 消费者 从阻塞队列中获得数据
    {

      // 先对临界区上锁
      LockGuard lockguard(_mutex);

      std::cout << "消费者进入等待" << std::endl;
      // 判断是否为空 为空等待
      while (IsEmpty())
      {
        _cwait_num++;
        _consumer_cond.Wait(_mutex);
        _cwait_num--;
        std::cout << "消费者被唤醒 拿数据喽 " << std::endl;
      }
      // 4. if(IsEmpty())不满足 || 线程被唤醒
      *out = _q.front();
      _q.pop();  
      // 肯定有空间 唤醒生产者生产数据
      if (_pwait_num)
      {
        cout << "唤醒生产者 生产数据" << endl;
        _productor_cond.Notify();
      }
   
    
    }
    ~BlockQueue()
    {
      // 释放锁和条件变量                     
    }             

  private:
    std::queue<T> _q;               // 保存数据的容器，临界资源
    int _cap;                       // bq最大容量
    Mutex _mutex;         // 互斥
    Cond _productor_cond; // 生产者条件变量
    Cond _consumer_cond;  // 消费者条件变量

    int _cwait_num; // 消费者等待计数器
    int _pwait_num; // 生产者等待计数器
  };
  // class BlockQueue
  // {
  // private:
  //   // 判满
  //   bool IsFull() { return _q.size() == _cap; }
  //   // 判空
  //   bool IsEmpty() { return _q.empty(); }

  // public:
  //   // 构造
  //   BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
  //   {
  //     // 初始化锁 和条件变量
  //     pthread_mutex_init(&_mutex, nullptr);
  //     pthread_cond_init(&_consumer_cond,nullptr);
  //     pthread_cond_init(&_productor_cond,nullptr);
  //   }
  //   void Equeue(const T &in) // 生产者 制造数据
  //   {
  //     // 对临界区上锁
  //     pthread_mutex_lock(&_mutex);
  //     // 你想放数据，就能放数据吗？？生产数据是有条件的！
  //     // 结论1: 在临界区中等待是必然的(目前) 因为要想制造数据必须要访问临界区(阻塞队列)
  //     while (IsFull()) // 5. 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
  //     {
  //       std::cout << "生产者进入等待" << std::endl;
  //       _pwait_num++;
  //       pthread_cond_wait(&_productor_cond, &_mutex); // 2.进入等待释放锁 出等待再加锁

  //     // 3. 返回，线程被唤醒&&重新申请并持有锁(它会在临界区内醒来！)
  //     std:
  //       cout << "生产者被唤醒" << std::endl;
  //     }
  //     // 4. while(IsFull())不满足 || 线程被唤醒
  //     // 生产
  //     _q.push(in);

  //     // 肯定有数据！ 此时经过判断消费者等待计数器唤醒消费者
  //     if (_cwait_num)
  //     {
  //       std::cout << "叫醒消费者" << std::endl;
  //       pthread_cond_signal(&_consumer_cond);
  //     }
  //     pthread_mutex_unlock(&_mutex);
  //   }
  //   void Pop(T *out) // 消费者 从阻塞队列中获得数据
  //   {

  //     // 先对临界区上锁
  //     pthread_mutex_lock(&_mutex);
  //     std::cout << "消费者进入等待" << std::endl;
  //     // 判断是否为空 为空等待
  //     while (IsEmpty())
  //     {
  //       _cwait_num++;
  //       pthread_cond_wait(&_consumer_cond, &_mutex);
  //       _cwait_num--;
  //       std::cout << "消费者被唤醒 拿数据喽 " << std::endl;
  //     }
  //     // 4. if(IsEmpty())不满足 || 线程被唤醒
  //     *out = _q.front();
  //     _q.pop();  
  //     // 肯定有空间 唤醒生产者生产数据
  //     if (_pwait_num)
  //     {
  //       cout << "唤醒生产者 生产数据" << endl;
  //       pthread_cond_signal(&_productor_cond);
  //     }
  //     pthread_mutex_unlock(&_mutex);
    
  //   }
  //   ~BlockQueue()
  //   {
  //     // 释放锁和条件变量
  //     pthread_mutex_destroy(&_mutex);
  //     pthread_cond_destroy(&_consumer_cond);
  //     pthread_cond_destroy(&_productor_cond);
  //   }

  // private:
  //   std::queue<T> _q;               // 保存数据的容器，临界资源
  //   int _cap;                       // bq最大容量
  //   pthread_mutex_t _mutex;         // 互斥
  //   pthread_cond_t _productor_cond; // 生产者条件变量
  //   pthread_cond_t _consumer_cond;  // 消费者条件变量

  //   int _cwait_num; // 消费者等待计数器
  //   int _pwait_num; // 生产者等待计数器
  // };
}
